#include <QFile>
#include "fingerprint.h"
#include "log.h"
#include "sha2.h"

using std::vector;

void FingerPrint::hash(QFile& file,
                       FingerPrint* finger) {

    if (!file.open(QIODevice::ReadOnly))
        return;
    if (!finger)
        finger = new FingerPrint;
    finger->init();
    while (!file.atEnd()) {
        QByteArray text = file.read(10000);
        finger->append((quint8*)text.data(), text.length());
    }
    finger->end();
    file.close();
}

void FingerPrint::hash(QFile& file,
                       FingerPrint* finger,
                       std::vector<FingerPrint>* fingers,
                       qint64 *size) {

    if (!file.open(QIODevice::ReadOnly))
        return;
    if (!finger)
        finger = new FingerPrint;
    
    if (!size) size = new qint64;
    *size = file.size();
    
    fingers = new std::vector<FingerPrint>;
    
    qint64 number_of_chunks =1;
    while ( ((int)(*size)/(int)number_of_chunks )>100000 && number_of_chunks<100) ++number_of_chunks; 
    
    qint64 chunk_size = (*size)/number_of_chunks;
    
    while (!file.atEnd()) {
        QByteArray text = file.read(chunk_size);
        FingerPrint finger;
        hash((quint8*)text.data(), text.length(), &finger);
        fingers->push_back( finger );
    }
    file.close();
}



void FingerPrint::hash(const quint8* text, int length,
                       FingerPrint* finger) {
    if (!finger)
        finger = new FingerPrint;
    finger->init();
    finger->append(text, length);
    finger->end();
}

void FingerPrint::init() {
    pctx = new sha256_ctx;
    CHECK_NE(pctx, NULL) << "Cannot allocate memory for pctx.";
    sha256_init(pctx);
}

void FingerPrint::append(const quint8 *text, int length){
    if (pctx)
        sha256_update(pctx, text, length);
}

void FingerPrint::end(){
    h[0] = pctx->h[0];
    h[1] = pctx->h[1];
    h[2] = pctx->h[2];
    h[3] = pctx->h[3];
    h[4] = pctx->h[4];
    h[5] = pctx->h[5];
    h[6] = pctx->h[6];
    h[7] = pctx->h[7];
    delete pctx;
}

bool FingerPrint::equals(const FingerPrint* finger) const {
    // sens?
    if (!finger)
        return false;

    for (int i = 0; i < 8; ++i)
        if (h[i] != finger->h[i])
            return false;
    return true;
}

FingerPrint* FingerPrint::clone() const{
	FingerPrint *fp = new FingerPrint;
	fp->h[0] = h[0];
	fp->h[1] = h[1];
	fp->h[2] = h[2];
	fp->h[3] = h[3];
	fp->h[4] = h[4];
	fp->h[5] = h[5];
	fp->h[6] = h[6];
	fp->h[7] = h[7];
	return fp;
}

void FingerPrint::sum(const std::vector<FingerPrint>& fingers) {
    vector<FingerPrint>::const_iterator iter = fingers.begin();
    while (iter!= fingers.end()) {
        h[0] ^= iter->h[0];
        h[1] ^= iter->h[1];
        h[2] ^= iter->h[2];
        h[3] ^= iter->h[3];
        h[4] ^= iter->h[4];
        h[5] ^= iter->h[5];
        h[6] ^= iter->h[6];
        h[7] ^= iter->h[7];
        ++iter;
    }
}

